<?php
/**
 * 实人验证日志表
 * Created on 2022/5/9 16:38
 * Created by 管昌虎
 * Email guanchanghu626@163.com
 * @author 管昌虎
 */

namespace GuanChanghu\Library\Models;

use GuanChanghu\Configs\SerialConfig;
use GuanChanghu\Exceptions\DeveloperException;
use GuanChanghu\Library\Facades\Territory;
use GuanChanghu\Configs\FieldConfig;
use GuanChanghu\Exceptions\UserException;
use Exception;
use GuanChanghu\Utils\Time\Facade as TimeUtil;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use JetBrains\PhpStorm\ArrayShape;
use Illuminate\Database\Eloquent\Builder;

/**
 * @author 管昌虎
 * Class AuthenticationLog
 * @tag encryption free
 * @package GuanChanghu\Library\Models
 * Created on 2022/5/9 16:40
 * Created by 管昌虎
 * Email guanchanghu626@163.com
 */
class AuthenticationLog extends \GuanChanghu\Models\Model
{
    /**
     * 数值精度处理
     *
     * @var string[]
     */
    protected $casts = [
        'user_id' => 'integer',
        'driver' => 'string',
        'certificate_type' => 'integer',
        'pay_driver' => 'string',
        'money' => 'float',
        'user_wallet_log_id' => 'integer',
        'award_user_wallet_log_id' => 'integer',
        'order_no' => 'string',
        'real_name' => 'string',
        'id_number' => 'string',
        'front_image' => 'string',
        'back_image' => 'string',
        'hand_image' => 'string',
        'status' => 'integer',
        'passed_at' => 'datetime',
        'refused_at' => 'datetime',
        'remark' => 'string',
    ];

    /**
     * 证件类型-身份证
     */
    public const CERTIFICATE_TYPE_IDENTITY_CARD = 0;

    /**
     * 证件类型-驾驶证
     */
    public const CERTIFICATE_TYPE_DRIVING_LICENCE = 1;

    /**
     * 证件类型-护照
     */
    public const CERTIFICATE_TYPE_PASSPORT = 2;

    /**
     * @return BelongsTo
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(config('guan-changhu.models.user'), 'user_id');
    }

    /**
     * @return BelongsTo
     */
    public function userWalletLog(): BelongsTo
    {
        return $this->belongsTo(config('guan-changhu.models.user_wallet_log'), 'user_wallet_log_id');
    }

    /**
     * @return BelongsTo
     */
    public function awardUerWalletLog(): BelongsTo
    {
        return $this->belongsTo(config('guan-changhu.models.user_wallet_log'), 'award_user_wallet_log_id');
    }

    /**
     * @return string
     */
    public function getDriverLabelAttribute(): string
    {
        return config('guan-changhu.authentication.drivers.' . $this['driver'] . '.label', '未知');
    }

    /**
     * @return string
     */
    public function getPayDriverLabelAttribute(): string
    {
        return config('guan-changhu.pay_driver.' . $this['driver'], '未知');
    }

    /**
     * @return string
     */
    public function getCertificateTypeLabelAttribute(): string
    {
        return config('guan-changhu.certificate_enum.' . $this['certificateType'], '未知');
    }

    /**
     * @return string
     */
    public function getStatusLabelAttribute(): string
    {
        return match ($this['status']) {
            FieldConfig::STATUS_UNDERWAY => '进行中',
            FieldConfig::STATUS_SUCCESS => '成功',
            FieldConfig::STATUS_FAIL => '失败',
            default => '未知',
        };
    }

    /**
     * @return array
     */
    public static function certificateTypeLabelMany(): array
    {
        $dest = [];
        foreach ((array)config('guan-changhu.certificate_enum') as $value => $label) {
            $dest[] = [
                'value' => $value,
                'label' => $label,
            ];
        }

        return $dest;
    }

    /**
     * @param User $user
     * @return Builder|\Illuminate\Database\Eloquent\Model|null
     */
    public static function getLastInfo(User $user): Builder|\Illuminate\Database\Eloquent\Model|null
    {
        return static::query()
            ->where('user_id', $user->getKey())
            ->where('status', FieldConfig::STATUS_SUCCESS)
            ->first();
    }

    /**
     * @return void
     * @throws Exception
     */
    public function pass(): void
    {
        if ($this['status'] === FieldConfig::STATUS_SUCCESS) {
            return;
        }
        $this->changes([
            'status' => FieldConfig::STATUS_SUCCESS,
            'passedAt' => now(),
        ]);
    }

    /**
     * @param string $remark
     * @return void
     * @throws Exception
     */
    public function refuse(string $remark): void
    {
        if ($this['status'] === FieldConfig::STATUS_FAIL) {
            return;
        }

        $this->changes([
            'status' => FieldConfig::STATUS_FAIL,
            'refusedAt' => now(),
            'remark' => $remark,
        ]);
    }

    /**
     * @param array $array
     * @return static
     * @throws Exception
     */
    public static function uniqueInput(array $array): static
    {
        // 这里检查是否重复
        if (
            static::query()
                ->where('certificate_type', $array['certificateType'])
                ->where('id_number', $array['idNumber'])
                ->where('status', FieldConfig::STATUS_SUCCESS)
                ->exists()
        ) {
            // 这里查到了认证信息抛出错误
            throw new UserException(__('guan-changhu::auth.authentication.id_number_pass'));
        }

        static::verifyIdNumber($array['idNumber'], static::CERTIFICATE_TYPE_IDENTITY_CARD);

        return static::initialize($array)->changes();
    }

    /**
     * @return array
     * @throws UserException
     */
    #[ArrayShape(['territory' => "array", 'birthday' => "\Carbon\Carbon|false", 'gender' => "int"])] public function idNumberAnalysis(): array
    {
        return static::idNumberExtract($this['idNumber']);
    }

    /**
     * @param string $idNumber
     * @return array
     * @throws UserException
     */
    #[ArrayShape(['territory' => "array", 'birthday' => "\Carbon\Carbon|false", 'gender' => "int"])] public static function idNumberExtract(string $idNumber): array
    {
        $territory = Territory::getTerritoryById(substr($idNumber, 0, 6) . '000000');
        if (!$territory) {
            $territory = Territory::getTerritoryById(substr($idNumber, 0, 4) . '00000000');
        }
        if (!$territory) {
            throw new UserException(__('guan-changhu::auth.authentication.id_number_territory_error'));
        }

        $birthday = TimeUtil::createByCarbonCompact(substr($idNumber, 6, 8));

        if (((int)substr($idNumber, 16, 1)) % 2 === 0) {
            $gender = User::GENDER_FEMALE;
        } else {
            $gender = User::GENDER_MALE;
        }


        return ['territory' => $territory, 'birthday' => $birthday, 'gender' => $gender];
    }


    /**
     * @param string $idNumber
     * @param int|null $certificateType
     * @throws DeveloperException
     * @throws UserException
     */
    public static function verifyIdNumber(string $idNumber, int|null $certificateType = null): void
    {
        if (is_null($certificateType)) {
            $certificateType = static::CERTIFICATE_TYPE_IDENTITY_CARD;
        }
        // 这里在查询身份证号合法吧
        if ($certificateType === static::CERTIFICATE_TYPE_IDENTITY_CARD) {
            if (strlen($idNumber) !== 18) {
                // 没查到身份证号不合法
                throw new UserException(__('guan-changhu::auth.authentication.id_number_length_error'));
            }

            if (!Territory::getTerritoryById(substr($idNumber, 0, 4) . '00000000')) {
                // 没查到身份证号不合法
                throw new UserException(__('guan-changhu::auth.authentication.id_number_territory_error'));
            }

            // 简单校验一下,最后一位合不合法
            $lastCode = strtoupper(substr($idNumber, -1));
            if (!in_array($lastCode, SerialConfig::ID_NUMBER_LAST_ONE_DICT, true)) {
                throw new UserException(__('guan-changhu::auth.authentication.id_number_check_code_error'));
            }

            // 校验出生年月
            $birthday = substr($idNumber, 6, 8);
            $birthdayTime = strtotime($birthday);
            $time = time();
            if (!$birthdayTime || $birthdayTime >= $time || $birthdayTime + 150 * 365 * 24 * 60 * 60 < $time) {
                throw new UserException(__('guan-changhu::auth.authentication.id_number_birthday_error'));
            }

            // 校验位
            $yArr = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
            $wArr = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
            $sum = 0;
            for ($i = strlen($idNumber) - 2; $i >= 0; $i--) {
                $sum += $idNumber[$i] * $wArr[$i];
            }
            $key = $sum % 11;
            if ($yArr[$key] !== $lastCode) {
                throw new UserException(__('guan-changhu::auth.authentication.id_number_check_code_bit_error'));
            }

            return;
        }

        throw new DeveloperException('暂不支持此证件类型校验');
    }
}
